﻿using System;
using System.Collections.Generic;
using System.Linq;
using FluentValidation;
using FluentValidation.Results;
using FluentValidation.Validators;
using LINQPad.OpenAccess.Helpers;

namespace LINQPad.OpenAccess.UI.ViewModel.Validators
{
    /// <summary>
    /// <see cref="IValidatorFactory"/> based validator.
    /// </summary>
    public class FactoryBasedValidator : NoopPropertyValidator
    {
        #region Private

        #region - Vars

        private readonly IValidatorFactory _validatorFactory;
        private readonly IDictionary<Type, ChildValidatorAdaptor> _validatorCache = new Dictionary<Type, ChildValidatorAdaptor>( );

        #endregion

        #endregion

        #region Public

        #region - Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="FactoryBasedValidator" /> class.
        /// </summary>
        /// <param name="validatorFactory">The validator factory.</param>
        public FactoryBasedValidator( IValidatorFactory validatorFactory )
        {
            Guard.ArgumentNotNull( validatorFactory, "validatorFactory" );

            this._validatorFactory = validatorFactory;
        }

        #endregion

        #endregion

        #region NoopPropertyValidator Members

        /// <summary>
        /// Validates the specified context.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns>The validation failures.</returns>
        public override IEnumerable<ValidationFailure> Validate( PropertyValidatorContext context )
        {
            var value = context.PropertyValue;
            if ( value != null )
            {
                var valueType = value.GetType( );
                ChildValidatorAdaptor adapter;
                if ( !this._validatorCache.TryGetValue( valueType, out adapter ) )
                {
                    var validator = this._validatorFactory.GetValidator( valueType );
                    this._validatorCache[valueType] = adapter = ( validator == null ? null : new ChildValidatorAdaptor( validator ) );
                }
                if ( adapter != null )
                    return adapter.Validate( context );
            }

            return Enumerable.Empty<ValidationFailure>( );
        }

        #endregion
    }
}
